﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using LTorrent.Extensions;

namespace LTorrent.Peers
{
    public enum MessageType
    {
        Choke = 0,
        Unchoke = 1,
        Intrested = 2,
        NotIntrested = 3,
        Have = 4,
        BitField = 5,
        Request = 6,
        Piece = 7,
        Cancel = 8,
        //DHT + Port = 9,
        KeepAlive = 10,
        Unhandled = 11,
        CloseSocket = 12

    }

    class ReceivedMessage
    {
        public MessageType MessageID
        {
            get;
            protected set;
        }

        public Byte[] LengthPrefix
        {
            get;
            protected set;
        }

#region BitField's property
            public Byte[] PieceMap
            {
                get;
                protected set;
            }
#endregion

#region Request's and Cancel's properties
        //Using also with Have messages
           public UInt32 Index
            {
                get;
                protected set;
            }

            public UInt32 Offset_begin
           {
               get;
               protected set;
           }

        public UInt32 Length
            {
                get;
                protected set;
            }
#endregion

#region Piece's property
        public Byte[] Block
        {
            get;
            protected set;
        }
//Also using Index and Offset_begin properties
#endregion

        /// <summary>
        /// КОнструктор. вызывает метод десериализации сообщения
        /// </summary>
        /// <param name="Message"></param>
        public ReceivedMessage(Byte[] Message)
        {
            Deserialize(Message);
        }

        protected void Deserialize(Byte[] Message)
        {
            if (Message.Length == 4)
            {
                MessageID = MessageType.KeepAlive;
                return;
            }

            MessageID = (MessageType)Message[4];
            if (Message[4] > 8 && Message[4] != 10)
            {
               // throw new ArgumentException("Can't identificate message!");
                MessageID = MessageType.Unhandled;
                Console.WriteLine("Can't initialize peer's answer");
                return;
            }

            LengthPrefix = Message.Take(4).ToArray();

            switch (MessageID)
            {

                case MessageType.BitField:
                    {

                        UInt32 Length = Message.Take(4).ToArray().fromNetworkOrder() - 1;
                        PieceMap = Message.Skip(5).ToArray();
                        if (PieceMap.Length != Length)
                        {
                            //throw new Exception("Incorrect Bitfield!");
                            MessageID = MessageType.CloseSocket;
                        }
                        return;
                    }

                case MessageType.Have:
                    {
                        Index = Message.Skip(5).Take(4).ToArray().fromNetworkOrder();
                        return;
                    }

                case MessageType.Cancel:
                case MessageType.Request:
                    {
                        Index = Message.Skip(5).Take(4).ToArray().fromNetworkOrder();
                        Offset_begin = Message.Skip(5 + 4).Take(4).ToArray().fromNetworkOrder();
                        Length = Message.Skip(5 + 4 + 4).Take(4).ToArray().fromNetworkOrder();
                        return;
                    }

                case MessageType.Piece:
                    {
                        Length = Message.Take(4).ToArray().fromNetworkOrder() - 9;
                        Index = Message.Skip(5).Take(4).ToArray().fromNetworkOrder();
                        Offset_begin = Message.Skip(5 + 4).Take(4).ToArray().fromNetworkOrder();
                        Block = Message.Skip(5 + 4 + 4).ToArray();
                        return;
                    }
                default:
                    return;
            }
            
        }
    }
}
